﻿using AlgoritmoConstrutivo.entidades;
using Entidades;
using RandomKeyGenerator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Algoritmos
{
    public class CabecaDeChaveCriterioRestritivo : Algoritmo
    {
        List<No> listaNosSemCLuster;

        /// <summary>
        /// Se o nó i for passado, é buscado a chave aleatória na posição i+n sendo n o número de módulos. Esta chave é multiplicada pelo número de clusters definido pela
        /// solução o que dará um número de 1 a NUM_CLUSTERS. Este será o id do cluster que se tentará alocar o módulo.
        /// </summary>
        /// <param name="individuo">identificador do indivíduo</param>
        /// <param name="noCorrente">Nó que está tentando-se alocar em um cluster</param>
        /// <param name="chaves"> vetor de chaves aleatórias</param>
        /// <param name="software">Grafo representando um software</param>
        /// <returns></returns>
        private Cluster RetornaPossivelCluster(int individuo, int noCorrente, Cromossomo[] chaves, Grafo software)
        {
            int idCluster = (int)Math.Ceiling(chaves[individuo].Chaves[noCorrente].KeyCriterio * NUM_CLUSTERS);
            if (idCluster == NUM_CLUSTERS)
                --idCluster;


            if (software.RetornarNumClusters() < idCluster || software.RetornarNumClusters() == idCluster)
                throw new Exception("Tentou-se retornar um cluster de id " + idCluster + " qdo só existem " + software.RetornarNumClusters());

            if (software.RetornarCluster(idCluster) == null)
                throw new NullReferenceException("O cluster escolhido no algoritmo ClusterCabecaDeChaveSimples é null");



            return software.RetornarCluster(idCluster);
        }



        private Cluster RetornaPossivelCluster(int noCorrente, Cromossomo cromossomo, Grafo software)
        {
            int idCluster = (int)Math.Ceiling(cromossomo.Chaves[noCorrente].KeyCriterio * NUM_CLUSTERS);
            if (idCluster == NUM_CLUSTERS)
                --idCluster;


            if (software.RetornarNumClusters() < idCluster || software.RetornarNumClusters() == idCluster)
                throw new Exception("Tentou-se retornar um cluster de id " + idCluster + " qdo só existem " + software.RetornarNumClusters());

            if (software.RetornarCluster(idCluster) == null)
                throw new NullReferenceException("O cluster escolhido no algoritmo ClusterCabecaDeChaveSimples é null");



            return software.RetornarCluster(idCluster);
        }



        


        public override Grafo Decoder(int numCromossos,Grafo software, Cromossomo cromossomo, int individuo,String estrategiaAlocacao,Configuration2 cfg)
        {
            software.IdOriginalCromossomo = individuo;

            base.DefineQtdeClusters(cromossomo, software);
            base.DefineCabecasChave(software, cfg);

            Cluster clUnitarios = new Cluster(true);
           
            this.listaNosSemCLuster = new List<No>();
            Array.Sort(cromossomo.Chaves);

            for (int noCorrente = 0; noCorrente < software.RetornarNumNos(); noCorrente++) //para cada J_esimo modulo do grafo do K_esimo cromossomo....
            {
                Gene rknoSemCluster = cromossomo.Chaves[noCorrente];
                No noSemCluster = software.RetornaNo(rknoSemCluster.Indice);

       

                //como já estou configurando alguns nós previamente, tenho que verificar se de fato eles nao tem clusters.
                if (noSemCluster.Cluster != null)
                    continue;

                Cluster clusterEscolhido = null;
                bool alocacao = false;

                clusterEscolhido = this.RetornaPossivelCluster(noCorrente, cromossomo, software);

                try
                {

                    alocacao = clusterEscolhido.AvaliaInsercaoDeUmNo(software, noSemCluster.Id);
                }
                catch (Exception ex)
                {

                    throw ex;
                }

                if (alocacao)
                {
                    software.ConfigurarClusterDeUmNo(clusterEscolhido, software.RetornaNo(noSemCluster.Id));

                }
                else
                    listaNosSemCLuster.Add(software.RetornaNo(noSemCluster.Id));
            }

            List<No> nosSemCluster2 = new List<No>();

            for (int noRestante = 0; noRestante < listaNosSemCLuster.Count; noRestante++) //para cada J_esimo modulo do grafo do K_esimo cromossomo....
            {
                for (int i = 0; i < software.RetornarNumClusters(); i++)
                {
                    try
                    {
                        if (software.RetornarCluster(i).AvaliaInsercaoDeUmNo(software, listaNosSemCLuster[noRestante].Id))
                        {
                            software.ConfigurarClusterDeUmNo(software.RetornarCluster(i), software.RetornaNo(listaNosSemCLuster[noRestante].Id));
                            listaNosSemCLuster.Remove(listaNosSemCLuster[noRestante]);

                            break;
                        }
                    }
                    catch (Exception ex)
                    {

                        throw ex;
                    }
                }
            }

            foreach (No n in listaNosSemCLuster)
            {
                if (n.ArestasQueSaem.Count > 0 && n.ArestasQueSaem[0].Cluster != null)
                {

                    try
                    {
                        software.ConfigurarClusterDeUmNo(n.ArestasQueSaem[0].Cluster, n);

                    }
                    catch (Exception ex)
                    {
                        
                        throw ex;
                    }

                }

                else if (n.ArestasQueEntram.Count > 0 && n.ArestasQueEntram[0].Cluster != null)
                {
                    software.ConfigurarClusterDeUmNo(n.ArestasQueEntram[0].Cluster, n);


                }
                else
                {
                    software.ConfigurarClusterDeUmNo(clUnitarios, n);

                }
            }
            base.ColocaModulosUnitariosMenosPiorPorCromossomo(cromossomo.Chaves, software);

            cromossomo.Chaves = cromossomo.Chaves.OrderBy(x => x.Indice).ToArray();
            //é necessário ordená-los de volta a partir de seus índices, para que o crossover possa ocorrer de forma correta. No crossover, 
            //comparamos a chave aleatório do módulo i do pai elite com a chave aleatoria do modulo i do pai nao elite. Se eu nao desordena-los agora, 
            //a chave aleatoria i do pai elite representa um modulo diferente da chave aleatoria i do pai nao elite

            return software; //tenho q validar se gero nós sem clusters.
            //tenho que decidir o que faço com a lista dos nós sem cluster.
        }

        public override void ConfigurarEstratégiaAlocacao(string estrategia)
        {
            throw new NotImplementedException();
        }

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, string estrategiaAlocacao)
        {
            throw new NotImplementedException();
        }

        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo[] chaves, int individuo)
        {
            throw new NotImplementedException();
        }



        public override Grafo Decoder(int numCromossos, Grafo software, Cromossomo cromossomo, int individuo, Configuration2 cfg)
        {
            throw new NotImplementedException();
        }
    }
}
